package parallelSRC

import fir.FirConfig
import spinal.core.{Mux, SFix, when, _}
import spinal.lib._
import spinal.core.sim._


case class ParallelSampleRateConversion(taps : Int, width : Int, coefficientConst : Array[Double]) extends Component {

  val romLines = coefficientConst.length / taps
  val io = new Bundle {
    //    val coefficient = slave Stream (Vec(SFix(0 exp, 16 bits), taps))
    //    val coefficient2 = slave Stream (Vec(SFix(0 exp, 16 bits), taps))
    val input = slave Stream SFix(width - 1 exp, width bits)      //输入信号
    val output = master Stream SFix(width - 1 exp, width bits)    //输出信号
    val interpolateRate = slave Stream UInt(log2Up(romLines) + 1 bits)//插值系数
    val decimateRate = slave Stream UInt(log2Up(romLines) + 1 bits)   //抽取系数
    //    val romAddress = out UInt (width bits)
    //    val rom2Address = out UInt (width bits)
  } simPublic() //io口Bundle
//  val lastOutput = Reg(SFix(width - 1 exp, width bits)) init 0
  val vecArr = Vec(Vec(SFix(0 exp, 16 bits), taps), romLines)

  for (i <- 0 until romLines) {
    for (j <- 0 until taps) {
      vecArr(i)(j) := coefficientConst(j * romLines + i) * romLines
    }
  }

  val outputCount = CounterFreeRun(2)
  var timeoutCount = 0
  if(romLines > 2){
    timeoutCount = (romLines + 1) / 2
  }else{
    timeoutCount = 2
  }
  io.output.valid := True

  //  io.coefficient.ready := True
  //  io.coefficient2.ready := True

  io.interpolateRate.ready := True
  io.decimateRate.ready := True
  io.input.ready := True
  val intRate = Reg(UInt(log2Up(romLines) + 1 bits)) init 0
  val decRate = Reg(UInt(log2Up(romLines) + 1 bits)) init 0

  val si = Vec(UInt(log2Up(romLines) + 1 bits),2)

  val stateI = Vec(Reg(UInt(log2Up(romLines) + 1 bits)) init 0, (romLines + 1) / 2)
  val state2I = Vec(Reg(UInt(log2Up(romLines) + 1 bits)) init 0, (romLines + 1) / 2)
  val currentState = Reg(UInt(log2Up((romLines + 1) / 2) + 1 bits)) init 0
  val useSame = si(0) < si(1)
  val dRateDouble = decRate + decRate
  val intRateDouble = intRate + intRate
  val shiftTwo = !useSame & (si(0) + dRateDouble > intRate & si(1) + dRateDouble > intRate)
  val allowToShift = (si(0) + dRateDouble > intRate | si(1) + dRateDouble > intRate) & outputCount.value === 0

  val inputReg = Reg(SFix(width - 1 exp, width bits)) init 0
  when(io.input.ready) {
    inputReg := io.input.payload
  }
  val serialToParallel = History(that = inputReg, length = 2, when = io.input.ready, init = S(0).toSFix)
  val regCom = ShiftRegChain(serialToParallel(1), serialToParallel(0), taps + 1, when = shiftTwo, allowToShift, init = S(0).toSFix)
  val hist = Vec(Vec(SFix(width - 1 exp, width bits), taps),2)
  for (i <- 0 until taps) {
    hist(1)(i) := Mux(useSame, regCom(i + 1), regCom(i))
    hist(0)(i) := regCom(i + 1)
  }
  si(0) := stateI(currentState.resized)
  si(1) := state2I(currentState.resized)

  when(io.interpolateRate.valid & io.interpolateRate.ready) {
    intRate := io.interpolateRate.payload
    stateI(0) := 0
    state2I(0) := decRate
    io.input.ready := False
    io.output.valid := False
  } otherwise {
    io.input.ready := True
  }

  when(io.decimateRate.valid & io.decimateRate.ready) {
    decRate := io.decimateRate.payload
    stateI(0) := 0
    state2I(0) := io.decimateRate.payload
    io.input.ready := False
    io.output.valid := False
  } otherwise {
    io.input.ready := True
  }

  for (i <- 0 until (romLines + 1) / 2 - 1) {
    when(stateI(i) + dRateDouble > intRateDouble) {
      stateI(i + 1) := (stateI(i) + dRateDouble) - intRateDouble
    } elsewhen (stateI(i) + dRateDouble > intRate) {
      stateI(i + 1) := (stateI(i) + dRateDouble) - intRate
    } otherwise {
      stateI(i + 1) := (stateI(i) + dRateDouble)
    }
    when(state2I(i) + dRateDouble > intRateDouble) {
      state2I(i + 1) := (state2I(i) + dRateDouble) - intRateDouble
    } elsewhen (state2I(i) + dRateDouble > intRate) {
      state2I(i + 1) := (state2I(i) + dRateDouble) - intRate
    } otherwise {
      state2I(i + 1) := (state2I(i) + dRateDouble)
    }
  }
  currentState.allowOverride
  currentState := Mux(outputCount.value === 0, Mux(currentState === (romLines + 1) / 2 - 1, U(0), currentState + 1), currentState)

  def FirFilter(index : UInt): SFix = new Area {
    val multiResult = (hist(index), vecArr(si(index).resized)).zipped.map((x, y) => x * y)
    val summaryResult = multiResult.reduceBalancedTree(_ + _)
  }.summaryResult

//  def TransposedFirFilter(index : UInt): SFix = new Area {
//    val multiResult = vecArr(si(index).resized).map(x => x * hist(index)(0))
//    val sumReg = Vec(Reg(SFix(width - 1 exp, width bits)) init (0), taps)
//    for (i <- 0 until taps - 1) {
//      sumReg(i) := (multiResult(i).truncated + sumReg(i + 1)).truncated
//    }
//    sumReg(taps - 1) := multiResult(taps - 1).truncated
//  }.sumReg(0)

  io.output.payload := 0
  when(io.output.fire){
    io.output.payload := FirFilter(U(1) - outputCount).resized.truncated//Mux(outputCount === 1, FirFilter(0), FirFilter(1)).resized.truncated
  }


}


object PSRCVerilogWithCustomConfig {
  def main(args: Array[String]) {
    val coefficientConst = Array[Double](
      -0.000262862825969905, 6.11523137053542e-05, 0.000565590346895291, 0.00146095565006740,
      0.00296270382571987, 0.00526470907677153, 0.00851291268392641, 0.0127821473845232,
      0.0180589041703863, 0.0242322593883240, 0.0310943652433647, 0.0383509117688612,
      0.0456409012211598, 0.0525640550027438, 0.0587133135993919, 0.0637092900253034,
      0.0672332672398216, 0.0690554238850042, 0.0690554238850042, 0.0672332672398216,
      0.0637092900253034, 0.0587133135993919, 0.0525640550027438, 0.0456409012211598,
      0.0383509117688612, 0.0310943652433647, 0.0242322593883240, 0.0180589041703863,
      0.0127821473845232, 0.00851291268392641, 0.00526470907677153, 0.00296270382571987,
      0.00146095565006740, 0.000565590346895291, 6.11523137053542e-05, -0.000262862825969905
    )
    FirConfig.generateVerilog(new ParallelSampleRateConversion(6,8, coefficientConst))

  }
}